import EditorUtil from './ckextraEditorUtil';
import RevisionItem from '../stores/ckextra/RevisionItem';

const {isNode, isText, isElement, isParagraph, isRoot, clearEmptyChar} = EditorUtil;

const createNodeInfo = (node, start, end) => {
  let type = RevisionItem.checkRevisionType(node);
  if (typeof(start) !== "undefined"){
    node = EditorUtil.getEditor().document.createText(node.getText().substring(start, end));
  } else {
    node = node && node.clone(true, true);
  }
  return {
    node, type
  };
}

const hasText = (node) => {
  return !!(node && clearEmptyChar(node.getText()));
}

const util = {
  getParentsFromRoot: (node) => {
    let path = EditorUtil.getEditor().elementPath(node);
    let reverse = [...path.elements].reverse();
    if (!node.equals(path.elements[0])) reverse.push(node);
    return reverse;
  },

  // 获得element的前面的有值的兄弟节点，如果兄弟节点不存在，则获得父的，层层向上找。
  // 最终找不到则返回null;
  getPreviousAcrossParents: (node) => {
    let prev = null;
    while(!hasText(prev)) {
      let nodeIndex = node.getIndex() - 1, parent = node.getParent();
      if (!parent) break;
      if (isParagraph(node)) {
        break;
      }
      for (nodeIndex; nodeIndex >=0; nodeIndex--) {
        prev = parent.getChild(nodeIndex);
        if (hasText(prev)) break;
      }
      node = parent;
      if (isRoot(node))
        break;
    }
    return hasText(prev) ? prev : null;
  },
  // 获得element的后面的有值的兄弟节点，如果兄弟节点不存在，则获得父的，层层向上找。
  // 最终找不到则返回null;
  getNextAcrossParents: (node) => {
    let next = null;
    while(!hasText(next)) {
      let nodeIndex = node.getIndex() + 1, parent = node.getParent();
      if (!parent) break;
      if (isParagraph(node)) {
        break;
      }
      for (nodeIndex; nodeIndex < parent.getChildCount(); nodeIndex++) {
        next = parent.getChild(nodeIndex);
        if (hasText(next)) break;
      }
      node = parent;
      if (isRoot(node))
        break;
    }
    return hasText(next) ? next : null;
    // let next = null;
    // while(!(next && !isParagraph(next) && next.getText()) && isNode(node)) {
    //   next = node.getNext();
    //   node = node.getParent();
    // }
    // return next || null;
  },
  // 获得前面有值的子节点与对应的offset
  getPreviousAcrossChildren: (node, offset) => {
    if (!isNode(node) || offset === 0) return {node: null, offset: -1};
    let result = {node, offset: offset - 1};
    if (isText(node)) {
      return result;
    }
    let child = null;
    for (let i=1; i<=offset; i++) {
      child = node.getChild(offset - i);
      if (EditorUtil.getContentLength(child) > 0) break;
    }
    return util.getPreviousAcrossChildren(child, EditorUtil.getContentLength(child));
  },
  getNextAcrossChildren: (node, offset) => {
    let result = {node, offset: offset + 1};
    if (isText(node)) {
      return result;
    }
    let child = node.getChild(offset);
    if (isNode(child)) {
      return util.getNextAcrossChildren(child, 0);
    } else {
      return result;
    }
  },

  /**
   * 获得共同父节点
   */
  getParentNode: (range) => {
    const { startContainer, endContainer } = range;
    const startParents = util.getParentsFromRoot(startContainer);
    const endParents = util.getParentsFromRoot(endContainer);
    // 寻找公共的根节点
    let parent = {};
    startParents.forEach((ele, index) => {
      if (index < endParents.length && ele.equals(endParents[index])) {
        parent = {
          root: ele,
          rootIndex: index,
        }
      }
    });
    return parent;
  },

  /**
   * 获得range范围内所有的节点
   * @param range
   * @returns {Array}
   */

  getRangeChildren: (range, needFindPostil) => { // needFindPostil: 直接删除的情况 会丢失postil 需要加回去
    // console.log('range', range);
    let nodeList = [];
    const preList = [];
    const afterList = [];
    const {startContainer, endContainer, startOffset, endOffset} = range;
    // 如果开始与结束在同一个节点下，则直接取该节点的text或子节点。
    if (startContainer.equals(endContainer)) {
      if (isText(startContainer)) {
        nodeList.push(createNodeInfo(startContainer, startOffset, endOffset));
      } else if (isElement(startContainer)) {
        // startOffset - 1 往前找有除了空的text节点外的postil start 节点，需要放入
        for (let i = startOffset - 1; i > 0; i--) {
          const child = startContainer.getChild(i);
          if (isText(child) && child.getText() === '') continue;
          if (isElement(child) && child.getName() === 'postil' && child.getAttribute('type') === 'start') {
            preList.push(createNodeInfo(child));
            break;
          } else {
            break;
          }
        }
        // startOffset开始到 endOffset前一个
        for (let i = startOffset; i < endOffset ; i++) {
          nodeList.push(createNodeInfo(startContainer.getChild(i)));
        }
        // endOffset往后找, 有除了空的text节点外的postil end 节点，需要放入
        for (let i = endOffset; i < startContainer.getChildCount() - 1; i++) {
          const child = startContainer.getChild(i);
          if (isText(child) && child.getText() === '') continue;
          if (isElement(child) && child.getName() === 'postil' && child.getAttribute('type') === 'end') {
            afterList.push(createNodeInfo(child));
            break;
          } else {
            break;
          }
        }
      }
      // 刚好选中前后postil包裹的一段，并且是输入内容的情况，不需要拼接前后的postil
      if ((preList.length > 0 && afterList.length === 0) || (preList.length === 0 && afterList.length > 0) || needFindPostil) {
        nodeList = [
          ...preList,
          ...nodeList,
          ...afterList,
        ]
      }
      nodeList = util.filterEmptyTextNodeOrUTag(nodeList);
      // console.log('nodeList', nodeList);
      return nodeList;
    }

    // 不在同一个节点下时，需要先找到两节点共同的父
    let startParents = util.getParentsFromRoot(startContainer),
      endParents = util.getParentsFromRoot(endContainer);
    // 寻找公共的根节点
    let root = null, rootIndex;
    startParents.forEach((ele, index) => {
      if (index < endParents.length && ele.equals(endParents[index])) {
        root = ele;
        rootIndex = index;
      }
    });

    if (!root) {
      return nodeList;
    }

    // 遍历父节点下的子节点，从开始位置起一一记录。
    let isStart, hasEnd, start = 0, end = root.getChildCount();
    if (startContainer.equals(root)) {
      start = startOffset;
      isStart = true;
    }
    if (endContainer.equals(root)) {
      end = endOffset;
      hasEnd = true;
    }
    for (let i=start;i<end;i++) {
      let child = root.getChild(i);

      // 没有找到开始节点
      if (!isStart) {
        // 比较此节点是否为开始节点或开始节点的父节点
        if (child.equals(startParents[rootIndex + 1])) {
          let length = startParents.length, j = length;
          do {
            j--;
            let node = startParents[j];

            // 如果是text则一定是startContainer.
            if (isText(node)) {
              nodeList.push(createNodeInfo(startContainer, startOffset));
            } else {
              let nodeStartOffset = j === (length - 1) ? startOffset : (startParents[j+1].getIndex() + 1);
              //节点后的所有子也需要一一记录。
              for (let k = nodeStartOffset;k< node.getChildCount();k++) {
                nodeList.push(createNodeInfo(node.getChild(k)));
              }
            }
          } while(j > rootIndex + 1)
          isStart = true;
        }
        continue;
      }
      // 已经找到开始结点
      // 如果没有找到结束节点，则 比较此节点是否为结束节点或结束节点的父节点
      if (!hasEnd && child.equals(endParents[rootIndex + 1])) {
        let j = rootIndex, length = endParents.length;
        do {
          j++;
          let node = endParents[j];

          // 如果是text则一定是endContainer.
          if (isText(node)) {
            nodeList.push(createNodeInfo(endContainer, 0, endOffset));
          } else {
            let nodeEndOffset = j === (length - 1) ? endOffset : endParents[j+1].getIndex();
            //节点后的所有子也需要一一记录。
            for (let k = 0; k < nodeEndOffset; k++) {
              const nodeChid = node.getChild(k);
              nodeList.push(createNodeInfo(node.getChild(k)));
            }
          }
        } while(j < length - 1);
        break;
      } else {
        // 中间节点直接记录即可。
        // 如果是 listAdvance节点 则 记录下的p节点 （修复带序号 删除的问题）
        const childNode = isElement(child) && child.getAttribute('listadvancetype') ? child.getChildren().toArray()[0] : child
        nodeList.push(createNodeInfo(childNode));
      }
    }

    nodeList = util.filterEmptyTextNodeOrUTag(nodeList, true);
    let childList = root.getChildren().toArray() || [];
    childList = util.filterEmptyTextNodeOrUTag(childList);
    // console.log('childList', childList, 'nodeList', nodeList)
    const startNode = nodeList[0].node;
    const endNode = nodeList[nodeList.length - 1].node;
    const startIndex = childList.findIndex(item => startNode.$.nodeName === item.$.nodeName && startNode.$.nodeValue === item.$.nodeValue);
    const endIndex = childList.findIndex(item => endNode.$.nodeName === item.$.nodeName && endNode.$.nodeValue === item.$.nodeValue);
    // console.log('startIndex', startIndex, 'endIndex', endIndex, 'nodeList', nodeList)
    // 得到的节点范围前后有当前nodeList外的postil标签的情况，需要把postil标签放入
    if (startIndex > -1) {
      for (let i = startIndex - 1; i > 0; i++) {
        const node = childList[i];
        if (isText(node) && node.getText() === '') {
          continue;
        }
        if (isElement(node) && node.getName() === 'postil' && node.getAttribute('type') === 'start') {
          if (!nodeList.some(item => isElement(item) && item.getName() === 'postil' && item.getAttribute('type') === 'start' && item.getAttribute('dataid') === node.getAttribute('dataid'))) {
            preList.push(createNodeInfo(node));
            break;
          }
        } else {
          break;
        } 
      }
    }
    if (endIndex > -1) {
      for (let i = endIndex + 1; i < childList.length; i--) {
        const node = childList[i];
        if (isText(node) && node.getText() === '') {
          continue;
        }
        if (isElement(node) && node.getName() === 'postil' && node.getAttribute('type') === 'end') {
          if (!nodeList.some(item => isElement(item) && item.getName() === 'postil' && item.getAttribute('type') === 'end' && item.getAttribute('dataid') === node.getAttribute('dataid') === 'end')) {
            preList.push(createNodeInfo(node));
            break;
          }
        } else {
          break;
        } 
      }
    }
    // 刚好选中前后postil包裹的一段，并且是输入内容的情况，不需要拼接前后的postil
    if ((preList.length > 0 && afterList.length === 0) || (preList.length === 0 && afterList.length > 0) || needFindPostil) {
      nodeList = [
        ...preList,
        ...nodeList,
        ...afterList,
      ]
    }
    // console.log('nodeList', nodeList);
    return nodeList;
  },

  // 过滤空的text 和 空的u
  filterEmptyTextNodeOrUTag: (nodeList, isObj) => { // isObj -》{node, type}
    nodeList = nodeList.filter(node => {
      if (isObj) node = node.node;
      const isEmptyText = isText(node) && node.getText() === '';
      const isEmptyUTag = isElement(node) && node.getName() === 'u' && node.getText() === '';
      if (!isEmptyText && !isEmptyUTag) {
        return node;
      }
    });
    return nodeList;
  },

  getPreviousOneRange: (range) => {
    const newRange = range.clone();
    let {startContainer, startOffset} = newRange;
    /**
     * 在while条件中找当前startContainer内的startOffset节点前一个字符的node与offset。
     * 如果没找到，则在while循环内部找startContainer之前的节点，
     * 找不到或者发现这已经是第一个节点则返回null
     * 找到了则以该节点为startContainer，节点长度为startOffset，来查找前一个字符的node与Offset。
     */
    let result = util.getPreviousAcrossChildren(startContainer, startOffset);
    let node = result.node, offset = result.offset;
    while (!node || offset < 0) {
      // 不处理换行
      if (EditorUtil.isParagraphFirst(startContainer)) return null;
      // 向前找，找到有值的节点。
      startContainer = util.getPreviousAcrossParents(startContainer);
      if (!startContainer) return null;
      startOffset = EditorUtil.getContentLength(startContainer);

      result = util.getPreviousAcrossChildren(startContainer, startOffset);
      node = result.node, offset = result.offset;
    }
    if (!node || offset < 0) return null;
    newRange.setStart(node, offset);
    return newRange;
  },
  getNextOneRange: (range) => {
    const newRange = range.clone();
    let {endContainer, endOffset} = newRange;
    if (endOffset === EditorUtil.getContentLength(endContainer)) {
      // 不处理换行
      if (EditorUtil.isParagraphLast(endContainer)) return null;
      // 找到后面的有值的节点
      endContainer = util.getNextAcrossParents(endContainer);
      if (!endContainer) return null;
      endOffset = 0;
    }
    let {node, offset} = util.getNextAcrossChildren(endContainer, endOffset);
    newRange.setEnd(node, offset);
    return newRange;
  },
  convertToNodeInfoList: (nodeList) => {
    if (!Array.isArray(nodeList) || nodeList.length === 0) return [];
    return nodeList.map((node) => createNodeInfo(node));
  },
  /**
   * 获得当前range的开始位置针对于paragraph文字的位置。
   * @returns {number}
   */
  getCurrentIndex: () => {
    const ranges = EditorUtil.getRanges();
    if (ranges.length === 0) return 0;
    let range = ranges[0];
    let {startContainer, startOffset} = range;
    let pList = [];// startContainer的父列表
    let p = startContainer;
    while(isNode(p) && !isParagraph(p)) {
      pList.push({p: p.getParent(), childIndex: p.getIndex()});
      p = p.getParent();
    }
    if (!isNode(p)) return 0;

    let index = 0;
    if (pList.length > 0) {
      pList.forEach((item) => {
        for (let i=0;i<item.childIndex;i++) {
          index += EditorUtil.clearEmptyChar(item.p.getChild(i).getText()).length;
        }
      });
    }
    if (isElement(startContainer)) {
      for (let i=0;i<startOffset;i++) {
        index += EditorUtil.clearEmptyChar(startContainer.getChild(i).getText()).length;
      }
    } else {
      let startText = startContainer.getText().substring(0, startOffset);
      index += EditorUtil.clearEmptyChar(startText).length;
    }
    return index;
  },

  getPositionByIndex: (index) => {
    let pos = null;
    const ranges = EditorUtil.getRanges();
    if (ranges.length === 0) return pos;
    let range = ranges[0];
    let {startContainer, startOffset} = range;
    let p = startContainer, pIndex = -1;
    while(isNode(p) && !isParagraph(p)) {
      pIndex = p.getIndex();
      p = p.getParent();
    }
    if (!isNode(p)) return pos;

    let node = p, remainIndex = index;
    while(pos === null) {
      if (isText(node)) {
        pos = {container: node, offset: remainIndex};
        if (node.getText().length < remainIndex){
          pos.offset = node.getText().length;
        }
      } else {
        for (let i =0;i<node.getChildCount();i++) {
          let child = node.getChild(i), text = child.getText();
          if (text.length === remainIndex) {
            pos = {container: node, offset: i+1};
            break;
          } else if (text.length < remainIndex) {
            remainIndex = remainIndex - text.length;
          } else {
            node = child;
            break;
          }
        }
      }
    }
    return pos;
  },

  /**
   * 排序，返回正数a在后，返回负数a在前，返回0则两者无顺序区别。
   * @param a
   * @param b
   * @returns {number}
   */
  sortNode: (a,b) => {
    if (a.equals(b)) return 0;
    let aParents = util.getParentsFromRoot(a),
      bParents = util.getParentsFromRoot(b);
    // 寻找公共的根节点
    let root = null, rootIndex;
    aParents.forEach((ele, index) => {
      if (index < bParents.length && ele.equals(bParents[index])) {
        root = ele;
        rootIndex = index;
      }
    });

    if (!root || a.equals(root) || b.equals(root)) return 0;
    let aIndex = aParents[rootIndex + 1].getIndex();
    let bIndex = bParents[rootIndex + 1].getIndex();
    return aIndex - bIndex;
  },

  /**
   * 从range中读取实际的开始的container，即在range范围内的第一个节点。
   * @param range
   */
  getStartContainer: (range) => {
    if (!range) {
      let ranges = EditorUtil.getRanges();
      range = ranges.length > 0 ? ranges[0] : null;
      if (range === null) return null;
    }
    let container = range.startContainer;
    if (EditorUtil.isElement(container)) {
      if (range.startOffset < container.getChildCount()) {
        // 向前找不用-1,直接用即可。
        container = container.getChild(range.startOffset);
      } else {
        if (!container.equals(range.endContainer)) {
          container = container.getNext() || container;
        }
      }
    }
    return container;
  },

  /**
   * 从range中读取实际的结束的container，即在range范围内的最后一个节点。
   * @param range
   */
  getEndContainer: (range) => {
    if (!range) {
      let ranges = EditorUtil.getRanges();
      range = ranges.length > 0 ? ranges[0] : null;
      if (range === null) return null;
    }
    let container = range.endContainer;
    if (EditorUtil.isElement(container)) {
      if (range.endOffset > 0) {
        container = container.getChild(range.endOffset - 1);
      } else {
        if (!container.equals(range.startContainer)) {
          container = container.getPrevious() || container;
        }
      }
    }
    return container;
  },
  isInInsertNode: () => {
    let startContainer = util.getStartContainer();
    if (EditorUtil.isText(startContainer)) {
      startContainer = startContainer.getParent();
    }
    if (EditorUtil.isParagraph(startContainer) || EditorUtil.isRoot(startContainer))
      return false;
    let type = RevisionItem.getNodeRevisionType(startContainer);
    return type === 'I';
  },
  isInEndInsertNode: () => {
    if(!CKEditorParams.editorObject.getSelection().getRanges()[0] 
      || !CKEditorParams.editorObject.getSelection().getRanges()[0].startContainer
      || !CKEditorParams.editorObject.getSelection().getRanges()[0].endContainer)
    return false;
    let {startContainer, endContainer,startOffset,endOffset} =CKEditorParams.editorObject.getSelection().getRanges()[0];
    //判断段落结尾
    if(startContainer 
      && startContainer.equals(endContainer)
      && startOffset > 0
      && startOffset === endOffset
      && typeof startContainer.getChildren === 'function') {
        if(startContainer.getChildren().getItem(startOffset) 
          && startContainer.getChildren().getItem(startOffset).getName 
          && startContainer.getChildren().getItem(startOffset).getName() == 'br'
          && startOffset > 0){
          startContainer = startContainer.getChildren().getItem(startOffset-1);
        }else if(startOffset > 0 
          && (startOffset === (startContainer.getChildren().count())
              && startContainer.getChildren().getItem(startOffset-1))
          || (startContainer.getChildren().getItem(startOffset-1) 
            && typeof startContainer.getChildren().getItem(startOffset-1).getName =='function'
            && startContainer.getChildren().getItem(startOffset-1).getName() == 'u')){
          startContainer = startContainer.getChildren().getItem(startOffset-1);
        }
    }
    if (EditorUtil.isParagraph(startContainer) || EditorUtil.isRoot(startContainer))
      return false;
    let type = RevisionItem.getNodeRevisionType(startContainer);
    return type === 'I';
  },
  isInTempTextNode: () => {
    let startContainer = util.getStartContainer();
    if (EditorUtil.isText(startContainer)) {
      startContainer = startContainer.getParent();
    }
    if (EditorUtil.isParagraph(startContainer) || EditorUtil.isRoot(startContainer))
      return false;
    let type = RevisionItem.getNodeRevisionType(startContainer);
    return type === 'T';
  }
};

export default util;